<template>
  <w-vertical :class="['w-calendar f-rel', 'w-calendar-' + this.x.face, {'z-showtime': this.showTime}]">
    <w-horizontal :class="['w-calendar-head', {'f-h-gap': dfish.br.compat.gap}]" valign="middle">
      <i class="w-calendar-head-prev f-i f-i-caret-left f-pointer" @click="clickStep(-1)"></i>
      <w-horizontal :class="['w-calendar-head-pop', {'f-h-gap': dfish.br.compat.gap}]">
        <div class="w-calendar-head-year f-link" ref="popYear" @click="clickPopYear()" v-html="headYear"></div>
        <w-dialog class="f-dfish-dialog" width="72" v-if="headPopYear" :context="$refs.popYear" :auto-hide="true"
          :ownproperty="true" :snap="{target: $refs.popYear, position: 'cc'}" @close="headPopYear=null">
          <div class="w-calendar-head-year-item" @click="clickPopYearStep(-1)"><i class="f-i f-i-minus"></i></div>
          <w-html height="*" @wheel.passive="mousewheelPopYear">
            <div v-for="item in headPopYear" :class="['w-calendar-head-year-item', {'z-on': item == dt.getFullYear()}]"
              @click="clickPopYearItem(item)">{{item}}</div>
          </w-html>
          <w-html class="w-calendar-head-year-item" @click="clickPopYearStep(1)"><i class="f-i f-i-plus"></i>
          </w-html>
        </w-dialog>
        <w-html v-if="this.x.face == 'date'" class="w-calendar-head-month f-link" ref="popMonth" @click="clickPopMonth">
          {{headMonth}}
        </w-html>
        <w-dialog class="f-dfish-dialog" width="72" v-if="headPopMonth" :context="$refs.popMonth" :auto-hide="true"
          :ownproperty="true" :snap="{target: $refs.popMonth, position: 'cc'}" @close="headPopMonth=null">
          <w-html height="*">
            <div v-for="item in headPopMonth"
              :class="['w-calendar-head-year-item', {'z-on': item == dt.getMonth() + 1}]"
              @click="clickPopMonthItem(item)">{{item}}</div>
          </w-html>
        </w-dialog>
      </w-horizontal>
      <i class="w-calendar-head-next f-i f-i-caret-right f-pointer" @click="clickStep(1)"></i>
      <div class="w-calendar-head-blank"></div>
      <div class="w-calendar-head-now f-link" @click="clickNow">{{loc.calendar.now}}</div>
    </w-horizontal>
    <slot name="head"></slot>
    <div class="w-calendar-body">
      <template v-if="this.x.face == 'date'">
        <div v-for="item in loc.calendar.day_title_1" class="w-calendar-daytitle">{{item}}</div>
      </template>
      <div v-for="item in bodyList"
        :title="item.title"
        :class="['w-calendar-item', {'z-on': item.focus && !item.disabled, 'z-pad': item.pad, 'z-ds': item.disabled}]"
        @click="clickItem($event,item)">
        <slot :item="item">
          <div class="w-calendar-item-text">{{item.text}}</div>
        </slot>
      </div>
    </div>
    <w-horizontal v-if="showTime" class="w-calendar-time f-abs-full">
      <w-vertical v-if="focusHour!=null" class="w-calendar-time-col" width="*">
        <div class="w-calendar-time-th">{{loc.hour}}</div>
        <w-buttonbar class="w-calendar-time-body" ref="hour" :current="focusHour" :vertical="true" :scroll-y="true"
          scroll-view=".z-on" height="*">
          <w-button class="w-calendar-time-item" v-for="h in Array(24).keys()" :key="h" :text="dfish.strPad(h)"
            :disabled="isTimeDisabled(h,'setHours')" @click="clickHour(h)" />
        </w-buttonbar>
      </w-vertical>
      <w-vertical v-if="focusMinute!=null" class="w-calendar-time-col" width="*">
        <div class="w-calendar-time-th">{{loc.minute}}</div>
        <w-buttonbar class="w-calendar-time-body" ref="minute" :current="focusMinute" :vertical="true" :scroll-y="true"
          scroll-view=".z-on" height="*">
          <w-button class="w-calendar-time-item" v-for="m in Array(60).keys()" :key="m" :text="dfish.strPad(m)"
            :disabled="isTimeDisabled(m,'setMinutes')" @click="clickMinute(m)" />
        </w-buttonbar>
      </w-vertical>
      <w-vertical v-if="focusSecond!=null" class="w-calendar-time-col" width="*">
        <div class="w-calendar-time-th">{{loc.second}}</div>
        <w-buttonbar class="w-calendar-time-body" ref="second" :current="focusSecond" :vertical="true" :scroll-y="true"
          scroll-view=".z-on" height="*">
          <w-button class="w-calendar-time-item" v-for="s in Array(60).keys()" :key="s" :text="dfish.strPad(s)"
            :disabled="isTimeDisabled(s,'setSeconds')" @click="clickSecond(s)" />
        </w-buttonbar>
      </w-vertical>
    </w-horizontal>
    <w-horizontal v-if="success" :class="['w-calendar-foot', {'f-h-gap': dfish.br.compat.gap}]" align="between" valign="middle">
      <div @click="clickShowTime" class="f-link" :style="{visibility:hasTime&&!onlyTime?'visible':'hidden'}">
        {{this.showTime ? loc.calendar.backdate : loc.calendar.picktime}}
      </div>
      <div class="f-link" @click="clickConfirm">{{loc.confirm}}</div>
    </w-horizontal>
  </w-vertical>
</template>

<script>
  import $ from '../dfish.js'
  import {
    h
  } from 'vue'
  import WidgetBase from './WidgetBase.vue'
  import Horizontal from './Horizontal.vue'

  export default {
    name: 'w-calendar',
    mixins: [WidgetBase],
    emits: ['click:item', 'change'],
    props: ['value', 'minValue', 'maxValue', 'current', 'pad', 'face', 'format', 'success', 'cg', 'start'],
    data() {
      let dt = this.value
      let bd = this.minValue
      let ed = this.maxValue
      let f = this.format || ''
      if (bd && (dt == null || dt < bd)) {
        dt = bd
      }
      if (ed && (dt == null || dt > ed)) {
        dt = ed
      }
      dt = dt ? $.dateParse(dt, f) : new Date()
      let onlyTime = /[Hms]/.test(f) && !/[yMd]/.test(f)
      return {
        dt,
        onlyTime,
        showTime: onlyTime,
        headPopYear: null,
        headPopMonth: null,
        focusHour: f.includes('H') ? dt.getHours() : null,
        focusMinute: f.includes('m') ? dt.getMinutes() : null,
        focusSecond: f.includes('s') ? dt.getSeconds() : null,
      }
    },
    watch: {
      value(v) {
        this.dt = v ? $.dateParse(v, this.formatter) : new Date()
      },
      dt(nv, ov) {
        let ny = nv.getFullYear()
        let oy = ov.getFullYear()
        if (this.x.face === 'year') {
          let n = ('' + ny).slice(-2, -1)
          let o = ('' + oy).slice(-2, -1)
          if (n != o) this.trigger('change', this, nv)
        } else if (this.x.face === 'month' || this.x.face === 'week') {
          if (ny != oy) this.trigger('change', this, nv)
        } else if (this.x.face === 'date') {
          if ((ny != oy) || (nv.getMonth() != ov.getMonth())) this.trigger('change', this, nv)
        }
      },
      current(nc) {
        if (!nc) return
        let nv = typeof nc === 'string' ? $.dateParse(nc, this.formatter) : nc
        let ov = this.dt
        let ny = nv.getFullYear()
        let oy = ov.getFullYear()
        if (this.x.face === 'year') {
          let n = ('' + ny).slice(-2, -1)
          let o = ('' + oy).slice(-2, -1)
          if (n != o) this.dt = nv
        } else if (this.x.face === 'month' || this.x.face === 'week') {
          if (ny != oy) this.dt = nv
        } else if (this.x.face === 'date') {
          if ((ny != oy) || (nv.getMonth() != ov.getMonth())) this.dt = nv
        }
      }
    },
    methods: {
      x_default() {
        let b = this.format || ''
        return {
          face: b === 'yyyy' ? 'year' : b.includes('MM') && !b.includes('dd') ? 'month' : b
            .includes('ww') ? 'week' : 'date'
        }
      },
      weekday(d) {
        let w = d.getDay()
        return w == 0 ? 6 : w - 1
      },
      clickStep(a) {
        let d = new Date(this.dt.getTime())
        if (this.x.face == 'year') {
          d = $.dateAdd(d, 'y', 10 * a)
        } else if (this.x.face == 'month') {
          d = $.dateAdd(d, 'y', a)
        } else if (this.x.face == 'date') {
          d = $.dateAdd(d, 'M', a)
        } else if (this.x.face == 'week') {
          d = $.dateAdd(d, 'y', a)
        }
        this.dt = d
      },
      clickPopYear(y) {
        y = y || (this.dt.getFullYear() - 4)
        if (y < 1000) y = 1000
        this.headPopYear = Array.from(Array(10)).map((v, i) => y + i)
      },
      clickPopYearItem(year) {
        let d = new Date(this.dt.getTime())
        d.setFullYear(year)
        this.dt = d
        this.headPopYear = null
      },
      clickPopYearStep(a) {
        let r = this.headPopYear
        let y = a < 0 ? r[0] - 10 : r[r.length - 1] + 1
        this.clickPopYear(y)
      },
      mousewheelPopYear(e) {
        this.clickPopYearStep(e.wheelDelta > 0 ? -1 : 1)
      },
      clickPopMonth() {
        this.headPopMonth = Array.from(Array(12)).map((v, i) => i + 1)
      },
      clickPopMonthItem(month) {
        let m = this.dt.getMonth()
        this.dt = $.dateAdd(this.dt, 'M', month - 1 - this.dt.getMonth())
        this.headPopMonth = null
      },
      clickNow() {
        this.dt = new Date()
        this.clickConfirm()
      },
      clickItem(e, item) {
        if (item.disabled) return
        if (this.x.current !== undefined) {
          this.dt = new Date(item.date.getTime())
        }
        e.runType = 'click:item'
        this.trigger(e, item)
        if (this.hasTime) this.clickShowTime()
        else if (this.x.success) {
          this.x.success(item.value)
        }
      },
      clickConfirm() {
        this.x.success && this.x.success(this.fm(this.dt))
      },
      clickShowTime() {
        this.showTime = !this.showTime
        this.focusHour = this.formatter.includes('H') ? this.dt.getHours() : null
        this.focusMinute = this.formatter.includes('m') ? this.dt.getMinutes() : null
        this.focusSecond = this.formatter.includes('s') ? this.dt.getSeconds() : null
      },
      clickHour(focusHour) {
        this.focusHour = focusHour
        this.dt.setHours(this.focusHour)
      },
      clickMinute(focusMinute) {
        this.focusMinute = focusMinute
        this.dt.setMinutes(this.focusMinute)
      },
      clickSecond(focusSecond) {
        this.focusSecond = focusSecond
        this.dt.setSeconds(this.focusSecond)
      },
      fm(a) {
        return $.dateFormat(typeof a === 'string' ? $.dateParse(a, this.formatter) : a, this.formatter)
      },
      isTimeDisabled(v, method) {
        let n = this.beginDate
        let m = this.endDate
        if (n || m) {
          let d = new Date(this.dt.getTime())
          d[method](v)
          d = this.fm(d)
          if ((n && d < n) || (m && d > m)) return true
        }
      },
    },
    computed: {
      loc() {
        return $.loc
      },
      formatter() {
        return this.x.format || 'yyyy-MM-dd'
      },
      hasTime() {
        return this.formatter.includes('H')
      },
      focusDate() {
        let c = this.x.current
        return c && typeof c === 'object' ? (this.x.format && this.fm(c)) : c
      },
      beginDate() {
        return this.x.minValue && this.fm(this.x.minValue)
      },
      endDate() {
        return this.x.maxValue && this.fm(this.x.maxValue)
      },
      headYear() {
        let y = this.dt.getFullYear()
        if (this.x.face === 'year') {
          y = this.dt.getFullYear() - (this.dt.getFullYear() % 10)
          y += ' - ' + (y + 9)
        }
        return $.loc.ps($.loc.calendar.y, y)
      },
      headMonth() {
        return $.loc.calendar.monthname[this.dt.getMonth()]
      },
      bodyList() {
        return this.x.face === 'year' ? this.bodyListYear : this.x.face === 'month' ? this.bodyListMonth :
          this.x.face === 'week' ? this.bodyListWeek : this.bodyListDate
      },
      bodyListYear() {
        let r = []
        let n = this.beginDate
        let m = this.endDate
        let f = this.focusDate
        let y = this.dt.getFullYear() - (this.dt.getFullYear() % 10) - 1
        let b = new Date(this.dt.getTime())
        b.setDate(1)
        b.setFullYear(y)
        for (let i = 0; i < 12; i++) {
          let v = y + i
          let g = {
            value: v,
            text: v,
            date: new Date(b.getTime()),
            pad: i == 0 || i == 11,
            disabled: (n && n > v) || (m && m < v) ? true : false,
            focus: f == v
          }
          r.push(g)
          b.setFullYear(b.getFullYear() + 1)
        }
        return r
      },
      bodyListMonth() {
        let r = []
        let n = this.beginDate
        let m = this.endDate
        let f = this.focusDate
        let y = this.dt.getFullYear()
        let b = new Date(this.dt.getTime())
        b.setDate(1)
        b.setMonth(0)
        for (let i = 0; i < 12; i++) {
          let v = this.fm(b)
          let g = {
            text: $.loc.calendar.monthname[i],
            value: v,
            date: new Date(b.getTime()),
            disabled: (n && n > v) || (m && m < v) ? true : false,
            focus: f == v
          }
          b.setMonth(b.getMonth() + 1)
          r.push(g)
        }
        return r
      },
      bodyListWeek() {
        let r = []
        let n = this.beginDate
        let m = this.endDate
        let f = this.focusDate
        let w = $.dateWeek(this.dt)
        let y = w[0]
        let b = $.dateWeekLast(y)
        let c = $.dateWeekFirst(y)
        let d = c[2]
        for (let i = 1, l = b[1]; i <= l; i++) {
          let v = this.fm(d)
          let g = {
            text: i,
            value: y + '-' + $.strPad(i),
            date: new Date(d.getTime()),
            disabled: (n && n > v) || (m && m < v) ? true : false,
            focus: f == v,
            title: $.loc.ps($.loc.calendar.from_to, $.dateFormat(d, 'yyyy-MM-dd'), $.dateFormat($.dateAdd(d, 'd', 6), 'yyyy-MM-dd'))
          }
          d.setDate(d.getDate() + 7)
          r.push(g)
        }
        return r
      },
      bodyListDate() {
        let b = new Date(this.dt.getTime())
        let c = b.getMonth()
        let d = new Date(b.getTime())
        let n = this.beginDate
        let m = this.endDate
        let f = this.focusDate
        let cell = []
        b.setDate(1)
        d.setDate(new Date(b.getFullYear(), c + 1, 0).getDate())
        let g = 7 - (this.weekday(b) + d.getDate()) % 7
        if (g && g < 7)
          d.setDate(d.getDate() + g)
        if (this.weekday(b) > 0)
          b.setDate(1 - this.weekday(b))
        if (this.x.pad) {
          g = 1 + (d.getTime() - b.getTime()) / 86400000
          g < 42 && d.setDate(d.getDate() + 42 - g)
        }
        while (b <= d) {
          let v = this.fm(b)
          let t = {
            text: b.getDate(),
            value: v,
            date: new Date(b.getTime()),
            pad: b.getMonth() !== c,
            disabled: (n && n > v) || (m && m < v) ? true : false,
            focus: f === v,
          }
          cell.push(t)
          b.setDate(b.getDate() + 1)
        }
        return cell
      }
    }
  }
</script>

<style>
  .w-calendar-dialog .w-calendar {
    padding: 0 10px;
  }

  .w-calendar {
    box-sizing: border-box;
  }

  .w-calendar-head,
  .w-calendar-foot {
    height: 36px;
    padding: 0 10px;
    --w-compat-gap: 8px;
    gap: var(--w-compat-gap);
  }

  .w-calendar-head-pop {
    --w-compat-gap: 8px;
    gap: var(--w-compat-gap);
  }

  .w-calendar-head-blank {
    flex: 1;
  }

  .w-calendar-head-month {
    margin-left: 3px;
  }

  .w-calendar-body {
    display: grid;
  }

  .w-calendar-date .w-calendar-body,
  .w-calendar-week .w-calendar-body {
    grid-template-columns: repeat(7, 1fr);
  }

  .w-calendar-month .w-calendar-body,
  .w-calendar-year .w-calendar-body {
    grid-template-columns: repeat(4, 1fr);
    border-top: 1px solid #ddd;
  }


  .w-calendar-dialog .w-calendar-month .w-calendar-body,
  .w-calendar-dialog .w-calendar-year .w-calendar-body {
    min-height: 120px;
  }

  .w-calendar-item {
    display: flex;
    flex-direction: column;
  }

  .w-calendar-item,
  .w-calendar-daytitle {
    text-align: center;
    padding: 5px;
    cursor: default;
  }

  .w-calendar-daytitle {
    background: #F8F8F8;
    border-bottom: 1px solid #ddd;
  }

  .w-calendar-item:not(.z-ds, .z-on):hover,
  .w-calendar-head-year-item:hover {
    background: #f5f5f5;
  }

  .w-calendar-item.z-on {
    background: var(--w--focus-color);
    color: #fff;
  }

  .w-calendar-item.z-ds,
  .w-calendar-item.z-pad {
    color: #999
  }

  .w-calendar-item-text {
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: center;
  }

  .w-calendar-foot {
    border-top: 1px solid #ddd;
  }

  .w-calendar-time {
    bottom: 35px;
    border-bottom: 1px solid #ddd;
  }

  .w-calendar-time-col {
    border-right: 1px solid #ddd;
  }

  .w-calendar-time-col:last-child {
    border: 0;
  }

  .w-calendar-time-th,
  .w-calendar-time-item {
    text-align: center;
    padding: 5px;
  }

  .w-calendar-time-th {
    border-bottom: 1px solid #ddd;
  }

  .w-calendar.z-showtime .w-calendar-head,
  .w-calendar.z-showtime .w-calendar-body {
    visibility: hidden;
  }

  .w-calendar-time-item {
    height: 30px;
    border: 0;
  }

  .w-calendar-time-item.z-on,
  .w-calendar-head-year-item.z-on {
    background: var(--w--focus-color);
    color: #fff;
  }

  .w-calendar-head-year-item {
    height: 24px;
    line-height: 24px;
    text-align: center;
  }
</style>
